<template>
	<el-container>
		<el-aside width="300px" v-loading="menuloading">
			<el-container>
				<el-header>
					<el-input placeholder="输入关键字进行过滤" v-model="menuFilterText" clearable></el-input>
				</el-header>
				<el-main class="nopadding">
					<el-tree ref="menuRef" class="menu" node-key="id" :data="menuList" :props="menuProps"
					         highlight-current :expand-on-click-node="false" check-strictly show-checkbox
					         :filter-node-method="menuFilterNode" @check-change="onCheckChange" @node-click="menuClick">
						<template #default="{node, data}">
							<span class="custom-tree-node">
								<span class="label">
									{{ node.label }}
								</span>
								<span class="do">
									<el-button icon="Plus" size="small" v-auth="'menu.create'"
									           @click.stop="createMenu(node, data)"></el-button>
								</span>
							</span>
						</template>

					</el-tree>
				</el-main>
				<el-footer style="height:51px;">
					<el-button type="primary" size="small" icon="Plus" v-auth="'menu.create'"
					           @click="createMenu()"></el-button>
					<el-button type="danger" size="small" plain icon="Delete" v-auth="'menu.delete'"
					           @click="delMenu"></el-button>
				</el-footer>
			</el-container>
		</el-aside>
		<el-container>
			<el-main class="nopadding" style="padding:20px;" ref="main">
				<Form ref="formRef" :menu="menuList" :update-menu-node="updateMenuNode"/>
			</el-main>
		</el-container>
	</el-container>
</template>

<script setup lang="ts">
import {onMounted, ref, watch, nextTick} from 'vue';
import {getMenuListApi, createMenuApi, deleteMenuApi} from "@/api/modules/setting.ts";
import {ElMessage, ElMessageBox} from "element-plus";
import {RequestMenuModel} from "@/models/settingModel.ts";
import Form from "@/views/setting/menu/components/form.vue";

// 状态
const menuloading = ref(false);
const menuList = ref<Menu.MenuOptions[]>([]);
const menuFilterText = ref('');
const menuRef = ref<any>(null);
const saveRef = ref<any>(null);
const mainRef = ref<HTMLElement | null>(null);
const formRef = ref<InstanceType<typeof Form> | null>(null);

const menuProps = {
	label: (data: Menu.MenuOptions) => data.title,
};

// 监听过滤文本
watch(menuFilterText, val => menuRef.value?.filter(val));

// 获取菜单
const getMenu = async () => {
	try {
		menuloading.value = true;
		const menuData = await getMenuListApi();
		menuList.value = menuData.data.data;
	} finally {
		menuloading.value = false;
	}
};

onMounted(getMenu);

// 复选框状态变化
const onCheckChange = async (data: Menu.MenuOptions, checked: boolean) => {
	if (checked) {
		const allNodes = menuRef.value.getCheckedNodes();
		allNodes.forEach((node: any) => {
			if (node.id !== data.id) {
				menuRef.value.setChecked(node, false, false);
			}
		});
	}
};

//点击树节点触发。
const menuClick = async (data: Menu.MenuOptions, node: any) => {
	const pid = node.level === 1 ? 0 : node.parent?.data?.id ?? 0;
	await nextTick();
	formRef.value?.setData(data, pid);
	if (mainRef.value) mainRef.value.scrollTop = 0;
};

// 搜索过滤
const menuFilterNode = (value: string, data: Menu.MenuOptions) => {
	if (!value) return true;
	return data.meta?.title.includes(value);
};

// 增加
const createMenu = async (node?: any, data?: Menu.MenuOptions) => {
	const menuName = '未命名_' + Math.random().toString(36).substring(2, 8);
	const newMenuData: RequestMenuModel = {
		parent_id: data?.id || 0,
		name: menuName,
		path: '路由地址',
		component: '',
		title: menuName,
		icon: '',
		active: '',
		tag: '',
		type: 'menu',
		is_hide: 'false',
		is_full: 'false',
		is_affix: 'false',
		is_keep_alive: 'false',
		sort: 0,
		state: 'normal',
		permission: [],
		role: [],
	};

	try {
		menuloading.value = true;
		const menuData = await createMenuApi(newMenuData);
		newMenuData.id = menuData.data.data.id;
		menuRef.value?.append(newMenuData, node);
		menuRef.value?.setCurrentKey(newMenuData.id);
		const pid = node ? node.data.id : 0;
		saveRef.value?.setData(newMenuData, pid);
	} catch (error) {
		console.log(error);
	} finally {
		menuloading.value = false;
	}
};

// 删除
const delMenu = async () => {
	const checkedNodes = menuRef.value.getCheckedNodes();
	if (!checkedNodes.length) {
		ElMessage.warning("请选择需要删除的项!");
		return;
	}

	const allNodes = menuRef.value.store._getAllNodes().map((n: any) => n.data);
	const hasChildren = checkedNodes.some((sel: any) =>
		allNodes.some((item: any) => item.parent_id === sel.id)
	);
	if (hasChildren) {
		await ElMessageBox.confirm("所选菜单中存在子菜单，请先删除子菜单后再操作。", '提示', {
			type: 'warning',
			confirmButtonClass: 'el-button--primary',
			showCancelButton: false,
		});
		return;
	}

	try {
		const confirm = await ElMessageBox.confirm('确认删除已选择的菜单吗?', '提示', {
			type: 'warning',
		});
		if (confirm === 'confirm') {
			menuloading.value = true;
			const menuData = await deleteMenuApi({id: checkedNodes[0].id});
			checkedNodes.forEach((item: any) => {
				const node = menuRef.value.getNode(item);
				if (node.isCurrent) formRef.value?.setData({id: 0}, 0);
				node.remove();
			});
			ElMessage.success(menuData.message);
		}
	} catch (error) {
		console.log(error);
	} finally {
		menuloading.value = false;
	}
};

const updateMenuNode = (updatedData: RequestMenuModel) => {
	const updateTree = (list: Menu.MenuOptions[]) => {
		list.forEach(item => {
			if (item.id === updatedData.id) {
				Object.assign(item, updatedData); // 更新节点数据
			} else if (item.children && item.children.length) {
				updateTree(item.children);
			}
		});
	};
	updateTree(menuList.value);
	// 如果当前节点正在编辑，也更新右侧表单
	const node = menuRef.value.getNode(updatedData.id);
	if (node?.isCurrent) {
		formRef.value?.setData({ ...updatedData }, updatedData.parent_id);
	}
};
</script>

<style scoped>
.custom-tree-node {
	display: flex;
	flex: 1;
	align-items: center;
	justify-content: space-between;
	font-size: 14px;
	height: 100%;
	padding-right: 24px;
}

.custom-tree-node .label {
	display: flex;
	align-items: center;;
	height: 100%;
}

.custom-tree-node .do {
	display: none;
}

.custom-tree-node .do i {
	margin-left: 5px;
	color: #999;
}

.custom-tree-node .do i:hover {
	color: #333;
}

.custom-tree-node:hover .do {
	display: inline-block;
}
</style>
